﻿using System.Text;

using RabbitMQ.Client;
using RabbitMQ.Client.Events;

namespace Tszy.Unlimited.Base.Infrastructure.Queues.RabbitMQ;

/// <summary>
/// RabbitMQ 消息消费者。
/// </summary>
public interface IRabbitMQConsumer
{
    /// <summary>
    /// 接收到消息时触发的事件。
    /// </summary>
    event EventHandler<RabbitMQRecivedEventArgs> Received;

    /// <summary>
    /// 如果是手动应答，则在应答执行前触发此事件。
    /// </summary>
    event EventHandler<RabbitMQRecivedEventArgs> AutoAckExecuting;

    /// <summary>
    /// 如果是手动应答，则在应答执行后触发此事件。
    /// </summary>
    event EventHandler<RabbitMQRecivedEventArgs> AutoAckExecuted;
}

/// <summary>
/// RabbitMQ 消息消费者。
/// </summary>
public class RabbitMQConsumer : IRabbitMQConsumer
{
    private readonly RabbitMQConfig _config;

    private readonly IModel _channel;

    /// <summary>
    /// 初始化。
    /// </summary>
    /// <param name="config">RabbitMQ 配置。</param>
    /// <param name="channel"></param>
    public RabbitMQConsumer(RabbitMQConfig config, IModel channel)
    {
        _config = config;
        _channel = channel;

        InitializeConsumer(_config.QueueName);
    }

    /// <summary>
    /// 接收到消息时触发的事件。
    /// </summary>
    public event EventHandler<RabbitMQRecivedEventArgs>? Received;

    /// <summary>
    /// 如果是手动应答，则在应答执行前触发此事件。
    /// </summary>
    public event EventHandler<RabbitMQRecivedEventArgs>? AutoAckExecuting;

    /// <summary>
    /// 如果是手动应答，则在应答执行后触发此事件。
    /// </summary>
    public event EventHandler<RabbitMQRecivedEventArgs>? AutoAckExecuted;

    ///// <summary>
    ///// 注册路由 KEY。
    ///// </summary>
    ///// <param name="routeKey">路由 KEY。</param>
    //public void Register(string routeKey)
    //{
    //    _channel.QueueBind(_config.QueueName, _config.ExchangeName, routeKey);
    //}

    private void InitializeConsumer(string queueName)
    {
        //_channel.QueueDeclare(queueName, _config.Durable, false, false, null);

        var consumer = new EventingBasicConsumer(_channel);
        consumer.Received += (model, eventArgument) =>
        {
            var eventBody = eventArgument.Body;

            var content = Encoding.UTF8.GetString(eventBody.ToArray());

            Received?.Invoke(eventArgument, new RabbitMQRecivedEventArgs
            {
                ExchangeName = eventArgument.Exchange,
                RoutingKey = eventArgument.RoutingKey,
                Content = content,
                Config = _config,
            });

            if (!_config.AutoAck)
            {
                AutoAckExecuting?.Invoke(eventArgument, new RabbitMQRecivedEventArgs
                {
                    Content = content,
                    Config = _config,
                });

                _channel.BasicAck(eventArgument.DeliveryTag, false);

                AutoAckExecuted?.Invoke(eventArgument, new RabbitMQRecivedEventArgs
                {
                    Content = content,
                    Config = _config,
                });
            }
        };

        _channel.BasicConsume(queueName, autoAck: _config.AutoAck, consumer: consumer);
    }
}